#include "stdafx.h"
#include "Book.h"


CBook::CBook()
{
    base_size = 0;
    base = (T_NODE *) malloc( MAX_BOOK_SIZE * sizeof(T_NODE) );
}


CBook::~CBook()
{
    free( base );
}

uint CBook::_FreeBase(  )
{
    base_size = 0;
    memset( base, 0, MAX_BOOK_SIZE * sizeof(T_NODE) );
    return 0;
}

uint CBook::Create( char inputfile[1024], char outputfile[1024], uint min_elo, uint ply_limit )
{
    max_ply     = ply_limit;
    min_rating  = min_elo;
    CTextFile input[1];
    // CTextFile output[1];
    std::cout << "CreateBook min-rating "<< min_rating <<", depth "<< ply_limit <<endl;
    _FreeBase();
    games_count = 0;
    if ( input->OpenToRead( inputfile ) )
    {
        std::cout << "Failed to open file " << inputfile << endl;
        return 1;
    } 
    // if ( output->OpenToBinaryReWrite( outputfile ) )
    // {
    //     std::cout << "Failed to write to file " << outputfile << endl;
    //     return 1;
    // }
    strcpy_s( game, 10, "");
    char line[65536];
    while(!input->EndOfFile())
    {
        input->ReadTextLine( line );
        _ParseLine( line );
        // cout << line << endl;
    }

    input->Close();

    // save book to output file
    uint ttt = base_size;
    
    cout << "base_size = " << base_size<<endl;
    CBookFile bookfile[1];
    bookfile->WriteBook( outputfile, base, base_size );

    //output->WriteBytes( &base_size, sizeof(base_size) );
    
    // for( uint e = 0; e < base_size; e++ )
    // {
    //     if ( base[e].key == 0 ) {
    //         cout << "oops"<<endl;
    //         cin>>ttt;
    //     }
    //     output->WriteBytes( &base[e], sizeof(T_NODE) );
    // }


    //output->Close();



    std::cout << endl;
    std::cout << "Done" <<endl;
    return 0;
}

uint CBook::_RstParser( )
{
    parser_state = 100;   // wait for tags
    welo=belo=0;        // wait for elo
    wwin = bwin = draw = 0;
    return 0;
}

uint CBook::_ParseValue( char * line, char * value )
{
    char * ptr;
    char * ptrend;
    ptr = strstr(line,"\"");
    if ( ptr )
    {
        ptr++;
        ptrend = strstr(ptr,"\"");
        if ( ptrend )
        {
            strcpy_s(ptrend, 4, "");
            sscanf_s (ptr,"%s",value);
            // cout << value  <<endl;
        } else return 1;
    } else return 1;
    return 0;
}

uint CBook::_IsTag( char * line )
{
    while( line[0] == ' ' ) line++;
    if ( line[0] == '[' )  return 1;
    else return 0;
}

uint CBook::_ParseLine( char * line )
{
    char value[65536];
    
    if ( _IsTag(line) )
    {
        if ( parser_state != 0 ) {
            _RstParser();
            parser_state = 0;
            strcpy_s( game, 10, "");            
        }
        if ( strstr (line,"WhiteElo") ) 
        { 
            _ParseValue( line, value );
            sscanf_s (value,"%d", &welo);
        } else if ( strstr (line,"BlackElo") ) 
        { 
            _ParseValue( line, value );
            sscanf_s (value,"%d", &belo);
        } else if ( strstr (line,"PlyCount") ) 
        { 
            _ParseValue( line, value );
            sscanf_s (value,"%d", &plycount);
        } else if ( strstr (line,"Result") ) 
        { 
            _ParseValue( line, value );
            // cout << "Result = "<< value<<endl;
            if ( strstr (value,"1-0") ) wwin = 1;
            else if ( strstr (value,"0-1") ) bwin = 1;
            else if ( strstr (value,"1/2-1/2") ) draw = 1;
        }
    } 
    else {
        if ( parser_state == 0 ){
            parser_state = 1;
            // cout << "WhiteElo = " << welo << endl;
            // cout << "BlackElo = " << belo << endl;
            // cout << "PlyCount = " << plycount << endl;
            // cout << "White wins = " << wwin << endl ;
            // cout << "Black wins = " << bwin << endl ;
            // cout << "Draws      = " << draw << endl ;
            _HouseKeepingLine( line );
            _ExtractAndCollectMoves( line, game );
            // cout << "Game Moves: "<< game<<endl;
            //_ParseMoves( line );
        } else {
            _HouseKeepingLine( line );
            _ExtractAndCollectMoves( line, game );
            // cout << "Game Moves: "<< game<<endl;
            //_ParseMoves( line );
        }

    }
    return 0;
}

uint CBook::_HouseKeepingLine( char * line )
{
    char * ptr = line;
    while( ptr[0] != '\n' && ptr[0] != '\0' ){
        if ( (ptr[0] == '.') || (ptr[0] == '+') || (ptr[0] == '#') )
            ptr[0] = ' ';
        ptr++;
    }
    return 0;
}

uint CBook::_ExtractAndCollectMoves( char * line, char * game )
{
    string s = line;
    char move[256];
    
    istringstream iss(s);

    while(iss >> move) {
        if ( ( !strcmp(move,"1-0") ) || ( !strcmp(move,"0-1") ) || ( !strcmp(move,"1/2-1/2") ) )
            _ParseMoves( game );
        else if ( (move[0] < '0') || (move[0] > '9') )
        {
            strcat_s( game, 65536, " ");
            strcat_s( game, 65536, move);

            // cout << move <<endl;
        }
    }
    return 0;
}

uint CBook::_ParseMoves( char * line )
{
    uint ply = 0;
    T_NODE node;
    games_total++;
    if ( (min_rating < welo) && (min_rating < belo) ) 
    {
        CBoard board[1];
        // cout << "Parse Game Moves: "<< line<<endl;
        CPGN pgn[1];
        CMoveList list[1];
        CMove m[1];
        CFen fen[1];
        char pgn_move[256];
        char listmove[256];
        uint dft;
        string s = line;
        istringstream iss(s);

        fen->Set( board, "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1");
        while(iss >> pgn_move) {
            m->GenAll( board, list );
            list->Start();
            uint nm = 0;
            //cout << endl;
            //cout << "Candidates: {"<<endl;
            while( !list->IsEmpty() ){
                pgn->MoveToPGN( board, list->GetMove(), listmove );
                //cout << listmove<< " ";
                if ( ( !strcmp(listmove,pgn_move) ) )
                    break;
                else { 
                    list->Next();
                }
            }
            //cout << "}" << endl;
            if ( list->IsEmpty() ){
                std::cout << "Cannot find move " << pgn_move << " in the following position:"<<endl;
                board->Print();
            
                cin >> dft;
                break;
            } else {
                if ( ply < max_ply )
                {
                    node.key    = board->GetHash();
                    node.wscore = wwin;
                    node.bscore = bwin;
                    node.draws  = draw;
                    _AddNodeToBook( node );
                    ply++;
                    m->Do( board, list->GetMove() );
                } 
                else break;
            }
        }
        games_count++;
        std::cout << "Games: "<< games_count << " ("<<games_total<<"),  Book Base = " << base_size <<"\r";
    }
    return 0;
}

uint CBook::_AddNodeToBook( T_NODE new_node )
{
    uint id = 0;
    while( id < base_size ){
        if ( base[id].key == new_node.key )
        {
            base[id].wscore += new_node.wscore;
            base[id].bscore += new_node.bscore;
            base[id].draws  += new_node.draws;
            return 0;
        }
        id++;
    }
    if ( base_size >= MAX_BOOK_SIZE ) return 1;
    base[base_size] = new_node;
    base_size++;
    return 0;
}

uint CBook::_SearchNodeInBook( T_NODE new_node )
{
    uint id = 0;
    uint tt;
    while( id < base_size ){
        // cout << "new_node.key = " << (uint64)new_node.key << ", base[id].key = "<<base[id].key<<endl;
        if ( base[id].key == 0 ) cin >> tt;
        if ( base[id].key == new_node.key )
            return id;
        id++;
    }
    return base_size;
}

uint CBook::GetMove( CBoard * board )
{
    CMoveList   list[1];
    CMove       gen[1];
    T_NODE      node;
    uint        num;
    uint        move_variant[ 256 ];
    uint        move_value[ 256 ];
    uint        move_count = 0;
    uint        rand_max = 0;
    uint        color = board->GetColor();
    cout << "Book_Moves:";
    gen->GenAll( board, list );
    while( !list->IsEmpty() )
    {
        if ( gen->Do( board, list->GetMove() ) ) // move is legal 
        {
            node.key = board->GetHash();
            //cout << ".";
            if (  ( (!board->IsDraw() ) ) && ( (num = _SearchNodeInBook( node )) != base_size )  ) // no repetition and found in base
            {  
                
                cout<<"["; list->PrintMove( list->GetMove() ); cout << "]";
                // cout << " ( +" << base[num].wscore << " -" << base[num].bscore << " =" << base[num].draws << " ) " ; 
                move_variant[move_count]    = list->GetMove();
                move_value[move_count]      = 
                    ( IS_WHITE(color) ) ? ( base[num].wscore + (base[num].draws >> 1)) :
                        ( base[num].bscore + (base[num].draws >> 1));
                if ( move_value[move_count] ) {
                    if ( ( IS_WHITE(color) && ( base[num].wscore >= base[num].bscore ) ) ||
                         ( IS_BLACK(color) && ( base[num].bscore >= base[num].wscore/3 ) ) )
                         {
                            rand_max += move_value[move_count];
                            // cout << "move_value = "<<move_value[move_count] << endl;
                            move_count++;
                        }
                    //else cout << "skipped"<<endl;
                } //else cout << "skipped"<<endl;
                
            }
            gen->Undo( board, list->GetMove());
        }
        list->Next();
    }
    cout << endl;
    //cout << "move_count = " << move_count <<endl;
    if ( move_count )
    {
        //cout << "rand_max = "<< rand_max << endl;
        uint point = rand() % rand_max;
        uint sel = 0;
        while( (sel < move_count) && ( point > move_value[sel] ) ) {
            point -= move_value[sel];
            sel++;
        }
        return move_variant[ sel ];
    } else return 0;
    return 0;
}

uint CBook::Load  ( char bookfile[1024] )
{
    base_size = 0;

    CBookFile file[1];
    base_size = file->ReadBook( bookfile, base );

    cout << "base_size = " << base_size<<endl;

    /*

    CTextFile input[1];
    if ( input->OpenToRead( bookfile ) )
    {
        std::cout << "Failed to open file " << bookfile << endl;
        return 1;
    } 

    //input->Test();
    //input->Close();
    //return 0;
    // load book from input file
    uint ttt;
    input->ReadBytes( &base_size, sizeof(base_size) );
    cout << "Book contains "<<base_size << " entries."<<endl;
    cout << "sizeof(T_NODE) = "<<sizeof(T_NODE)<<endl;
    if ( base_size ){
        for ( uint e=0; e < base_size; e++ ){
            cout << e << ". Read number of bytes : " << (input->ReadBytes( &base[e], sizeof(T_NODE) ))<<endl ;
           // if ( base[e].key == 0 ) {
           //     cout << "oops " << e <<endl;
           //     cin>>ttt;
           // }
        }
    }
    input->Close();

    */

    return 0;
}